/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年5月28日
 */
package com.massyframework.beanskin.runtime.framework;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.massyframework.beanskin.ModuleIdentifier;
import com.massyframework.beanskin.ModuleRepository;
import com.massyframework.beanskin.ModuleRuntimeException;
import com.massyframework.beanskin.SimpleModuleIdentifier;
import com.massyframework.beanskin.StartStopAbility;
import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.assembly.AssemblyBuilder;
import com.massyframework.beanskin.assembly.beans.BeanContextHandlerFactory;
import com.massyframework.beanskin.framework.Framework;
import com.massyframework.beanskin.framework.FrameworkConfig;
import com.massyframework.beanskin.framework.FrameworkEvent;
import com.massyframework.beanskin.framework.FrameworkListener;
import com.massyframework.beanskin.runtime.ModuleRegistry;
import com.massyframework.beanskin.service.ExportServiceDescriptor;
import com.massyframework.beanskin.service.ServicePropertiesBuilder;
import com.massyframework.beanskin.service.ServiceRegistry;
import com.massyframework.beanskin.service.ServiceRepository;
import com.massyframework.beanskin.service.ServiceRepositoryFactory;
import com.massyframework.beanskin.util.BeanSkinServiceLoaderUtils;
import com.massyframework.beanskin.util.classloading.ModuleClassLoader;
import com.massyframework.beanskin.util.exceptioncollect.ExceptionCollectUtils;
import com.massyframework.beanskin.util.version.VersionRange;
import com.massyframework.beanskin.util.threadlocal.ThreadLocalUtils;
import com.massyframework.beanskin.util.threadlocal.GlobalThreadLocalBinderFactory;

/**
 * 抽象的运行框架
 * @author huangkh
 *
 */
abstract class AbstractFramework extends ModuleRepositoryWrapper 
	implements Framework {

	private Logger logger = LoggerFactory.getLogger("[massy framework]");
	private ModuleRepository moduleRepository;
	private ServiceRepositoryFactory serviceRepositoryFactory;
	private Set<FrameworkListener> listeners;
	private Assembly kernel;
	
	private Set<ModuleIdentifier> deployModules;
	
	/**
	 * 构造方法
	 */
	@SuppressWarnings("unchecked")
	protected AbstractFramework(Builder builder) {
		this.moduleRepository = Objects.requireNonNull(
				builder.moduleRepository, "\"moduleRepository\" cannot be null.");
		this.serviceRepositoryFactory = Objects.requireNonNull(
				builder.serviceRepositoryFactory, "\"serviceRepositoryFactory\" cannot be null.");
				
		this.init(builder.frameworkConfig);

		
		List<FrameworkListener> list = ((DefaultFrameworkConfig)builder.frameworkConfig).frameworkListeners;
		this.listeners = new CopyOnWriteArraySet<>(list);
		Set<ModuleIdentifier> set =
				(Set<ModuleIdentifier>)builder.frameworkConfig.getAttribute(FrameworkConfig.DEPLOY_MODULES, Set.class);
		this.deployModules = set == null ? Collections.emptySet() : set;
	}
	
	protected void init(FrameworkConfig frameworkConfig) {
		this.kernel= this.createKernelAssembly(frameworkConfig);
		//注册moduleRepository
		ServiceRepository repo = ServiceRepository.retrieveFrom(this.kernel);
		GlobalThreadLocalBinderFactory factory = new GlobalThreadLocalBinderFactory(repo);
		ThreadLocalUtils.bindThreadLocalBinderFactory(factory);
		
		ServiceRegistry registry = repo.getServiceRegistry();
		Map<String, Object> props = ServicePropertiesBuilder.newInstance()
				.description("模块资源库")
				.topRanking()
				.build();
		registry.register(ModuleRepository.class, this.moduleRepository, props);
	}
	

	@Override
	public void destory() {
		this.listeners.clear();
		this.serviceRepositoryFactory = null;
		this.moduleRepository = null;
		this.kernel = null;
	}

	@Override
	protected ModuleRepository getModuleRepository() {
		return this.moduleRepository;
	}
	
	/**
	 * 服务资源库工厂
	 * @return {@link ServiceRepositoryFactory}
	 */
	protected ServiceRepositoryFactory getServiceRepositoryFactory() {
		return this.serviceRepositoryFactory;
	}
	
	/**
	 * 内核装配件
	 * @return {@link Assembly}
	 */
	protected Assembly getKernelAssembly() {
		return this.kernel;
	}
	
	/**
	 * 部署的模块，可能返回null.
	 * @return {@link Set}
	 */
	protected Set<ModuleIdentifier> getDeployModules(){
		return this.deployModules;
	}
	
	/**
	 * 清除部署模块
	 */
	protected void clearDeployModules() {
		this.deployModules.clear();;
	}
		
	/**
	 * 创建内核装配件
	 * @param config {@link FrameworkConfig},运行框架配置
	 * @return {@link Assembly}
	 */
	private Assembly createKernelAssembly(FrameworkConfig config) {
		Assembly result = null;
		AssemblyBuilder builder = config.getAttribute(AssemblyBuilder.class.getName(), AssemblyBuilder.class);
		if (builder == null) {
			builder = BeanSkinServiceLoaderUtils.loadService(AssemblyBuilder.class, this.getClass().getClassLoader());
		}
		
		ModuleIdentifier identifier =
				SimpleModuleIdentifier.createModuleIdentifierWithKernel();
		try {
			BeanContextHandlerFactory factory =
					config.getBean(BeanContextHandlerFactory.class.getName(), BeanContextHandlerFactory.class);
			builder.beanContext("simple", VersionRange.EMPTY, factory);
			builder.identifier(identifier);
			builder.name("装配件-基础设施-内核");
			builder.description("MassyFramework提供基于进程内的服务治理架构，通过类库、插件、装配件、片段实现可模块化的应用.");
			
			Set<String> tags = new HashSet<String>();
			tags.add("模块化");
			tags.add("内核");
			tags.add("服务治理");
			builder.addTags(tags);
			
			builder.classLoader((ModuleClassLoader)this.getClass().getClassLoader());
			builder.moduleRepository(this.moduleRepository)
				.serviceRepositoryFactory(this.serviceRepositoryFactory);
			
			
			//初始化参数
			List<String> names = config.getParameterNames();
			for (String name: names) {
				builder.setParameter(name, config.getParameter(name));
			}
			
			//Bean实例
			List<String> beanNames = config.getBeanNames();
			for (String beanName: beanNames) {
				if (beanName.equals(ModuleRepository.class.getName())) {
					continue;
				}
				
				Object bean = config.getBean(beanName);
				builder.addBean(beanName, bean);
				
				//输出服务
				ExportServiceDescriptor descriptor = config.getExportServiceDescriptor(beanName);
				if (descriptor != null) {
					builder.addExportService(descriptor);
				}
			}
			
			result = builder.build();
			if (this.moduleRepository instanceof ModuleRegistry) {
				((ModuleRegistry)this.moduleRepository).register(result);
			}
			
			((StartStopAbility)result).start();
		}catch(Exception e) {		
			ModuleRuntimeException me = new ModuleRuntimeException(identifier, e);
			ExceptionCollectUtils.addException(me);
			if (logger.isErrorEnabled()) {
				logger.error("启动内核装配件出错：" + e.getMessage(), e);
			}
		}

	
		return result;
	}
	
	
	
	@Override
	public void addListener(FrameworkListener listener) {
		if (listener != null) {
			this.listeners.add(listener);
		}
	}

	@Override
	public void removeListener(FrameworkListener listener) {
		if (listener != null) {
			this.listeners.remove(listener);
		}
		
	}

	/**
	 * 发布运行框架事件
	 * @param event {@link FrameworkEvent}
	 */
	public void applyEvent(FrameworkEvent event) {
		for (FrameworkListener listener: this.listeners) {
			try {
				listener.onEvent(event);
			}catch(Exception e) {
				if (logger.isErrorEnabled()) {
					logger.error(e.getMessage(), e);
				}
				ExceptionCollectUtils.addException(
						new ModuleRuntimeException(this.getKernelAssembly(), e));
			}
		}
	}
	
	/**
	 * 构建器
	 */
	public static class Builder {
		private FrameworkConfig frameworkConfig;
		private ModuleRepository moduleRepository;
		private ServiceRepositoryFactory serviceRepositoryFactory;
		
		/**
		 * 设置运行框架配置器
		 * @param value {@link FrameworkConfig} 
		 * @return {@link Builder}
		 */
		public Builder frameworkConfig(FrameworkConfig value) {
			this.frameworkConfig = value;
			return this;
		}
		
		/**
		 * 设置模块资源库
		 * @param value {@link ModuleRepository}
		 * @return {@link Builder}
		 */
		public Builder moduleRepository(ModuleRepository value) {
			this.moduleRepository = value;
			return this;
		}
		
		/**
		 * 设置服务资源库工厂
		 * @param value {@link ServiceRepositoryFactory
		 * @return {@link Builder}
		 */
		public Builder serviceRepositoryFactory(ServiceRepositoryFactory value) {
			this.serviceRepositoryFactory = value;
			return this;
		}
	}
}
